<!DOCTYPE html>
<html lang="en"><head>
    <title>Map of {{ org_repo }} contributors</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
    <style>
    footer, h1 { text-align: center; font-family: Courier; }
    #contributors-map { height: 35rem; max-width: 64rem; margin: 0 auto; }
    </style>
</head>
<body>
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
    <h1>Contributors of the <a href="https://github.com/{{ org_repo }}">{{ org_repo }}</a> project</h1>
    <div id="contributors-map"></div>
    <footer>Generated by a Python script &amp; LeafletJS - <a href="https://github.com/py-pdf/fpdf2/tree/master/contributors">sources</a></footer>
    <script>
    const ACCESS_TOKEN = 'pk.eyJ1IjoibGNpbW9uIiwiYSI6ImNrcWYxYXN2YjE5azMyd25tajE4NTk4cWMifQ.ur-iSyNaDXYhc7XDfziEYg';
    const EMOJI_KEY = {{ EMOJI_KEY | tojson }};

    const contributors = [{% for name, contributor in user_locations.items() if contributor.location %}
    {
        name: '{{ name }}',
        contributions: {{ contributor.contributions | safe }},
        location: '{{ contributor.location | safe }}',
        pulls: {{ contributor.pulls }},
        issues: {{ contributor.issues }},
    },{% endfor %}];

    const cMap = L.map('contributors-map').setView([0, 0], 2);

    L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
        maxZoom: 18,
        id: 'mapbox/streets-v11',
        tileSize: 512,
        zoomOffset: -1,
        accessToken: ACCESS_TOKEN,
    }).addTo(cMap);

    const geocoder = L.Control.Geocoder.nominatim();
    function placeNextContributor() {
        const contributor = contributors[0];
        geocoder.geocode(contributor.location, (results) => {
            const topResult = results[0];
            console.log(`geocoding ${contributor.name} topResult: ${JSON.stringify(topResult, null, 2)}`);
            if (topResult) {
                let popupHtml = `<a href="https://github.com/${contributor.name}" target="_blank"><b>${contributor.name}</b></a><br>`;
                if (contributor.location) {
                    popupHtml += `${contributor.location}<br>`;
                }
                if (contributor.contributions.length) {
                    popupHtml += `Contributions: ${contributor.contributions.map(c => `<span title="${c}">${EMOJI_KEY[c]}</span>`).join(" - ")}<br>`;
                }
                if (contributor.issues || contributor.pulls) {
                    popupHtml += `<a href="https://github.com/py-pdf/fpdf2/issues?q=author%3A${contributor.name}" target="_blank">`;
                }
                if (contributor.issues) {
                    popupHtml += `Opened ${contributor.issues} issue`;
                    if (contributor.issues > 1) popupHtml += 's';
                }
                if (contributor.pulls) {
                    popupHtml += contributor.issues ? ' and s' : 'S';
                    popupHtml += `ubmitted ${contributor.pulls} PR`;
                    if (contributor.pulls > 1) popupHtml += 's';
                }
                if (contributor.issues || contributor.pulls) {
                    popupHtml += `</a>`;
                }
                L.marker(jitterSimilarPositions(topResult.center)).bindPopup(popupHtml).addTo(cMap);
            }
            contributors.shift();
            if (contributors.length > 0) {
                placeNextContributor();
            }
        });
    }
    placeNextContributor();

    /*
     * Sometimes, several users have the exact same location.
     * For those cases, we introduce some jitter in their lat/lng coordinates
     * in order for the map markers to be visually distinct.
     */
    const POS_INCRS = [
        { dlat: 0.1, dlng: 0.1},
        { dlat: 0.1, dlng: -0.1},
        { dlat: -0.1, dlng: -0.1},
        { dlat: -0.1, dlng: 0.1},
    ];
    const positionsVisited = new Set();
    function jitterSimilarPositions(pos) {
        const origLat = pos.lat, origLng = pos.lng;
        let posStr = `${pos.lat}-${pos.lng}`;
        let incr_i = 0;
        let incr_factor = 1;
        while (positionsVisited.has(posStr)) {
            pos.lat = origLat + POS_INCRS[incr_i].dlat * incr_factor;
            pos.lng = origLng + POS_INCRS[incr_i].dlng * incr_factor;
            posStr = `${pos.lat}-${pos.lng}`;
            incr_i++;
            if (incr_i === POS_INCRS.length) {
                incr_factor++;
                incr_i = 0;
            }
        }
        positionsVisited.add(posStr);
        return pos;
    }
    </script>
</body></html>